//
// Created by ct on 2021/9/28.
//
#include "lib/common.h"

#define MAXEVENTS 128
#define LOOP for(int in_for_I =0 ; in_for_I < 100; ++in_for_I)

static char rot13_char(char c) {
    if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
        return (char)(c + 13);
    else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
        return (char)(c - 13);
    else
        return c;
}
/*
 * EPOLLIN：表示对应的文件描述字可以读；
 * EPOLLOUT：表示对应的文件描述字可以写；
 * EPOLLRDHUP：表示套接字的一端已经关闭，或者半关闭；
 * EPOLLHUP：表示对应的文件描述字被挂起；
 * EPOLLET：设置为 edge-triggered，边缘触发。开启这个服务器程序，用 telnet 连接上，输入一些字符，服务器端只从 epoll_wait 中苏醒过一次，就是第一次有数据可读的时候。
 * 默认为 level-triggered。不断地从 epoll_wait 中苏醒，告诉我们有数据需要读取。
 */
//char rot13_char(char c);

int main(int argc, char *argv[]) {
    int listen_fd, socket_fd;
    int n, i;
    int efd;
    struct epoll_event event;
    struct epoll_event *events;

    listen_fd = tcp_nonblocking_server_listen(SERV_PORT);

    efd = epoll_create(1);

    if (-1 == efd) {
        error(1, errno, "epoll create failed");
    };

    event.data.fd = listen_fd;
    event.events = EPOLLIN | EPOLLET;
    /*
     * EPOLL_CTL_ADD： 向 epoll 实例注册文件描述符对应的事件；
     * EPOLL_CTL_DEL：向 epoll 实例删除文件描述符对应的事件；
     * EPOLL_CTL_MOD： 修改文件描述符对应的事件。
     *  just for fun
     */
    if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &event)) {
        error(1, errno, "epoll_ctl add listen fd failed");
    }
    /* Buffer where events are returned */
    events = calloc(MAXEVENTS, sizeof(event));

    LOOP {
        n = epoll_wait(efd, events, MAXEVENTS, -1);
        printf("epoll_wait wakeup \n");

        for (i = 0; i < n; ++i) {
            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
                fprintf(stderr, "epoll error\n");
                close(events[i].data.fd);
                continue;
            } else if (listen_fd == events[i].data.fd) {
                struct sockaddr_storage ss;
                socklen_t slen = sizeof(ss);
                int fd = accept(listen_fd, (struct sockaddr *) &ss, &slen);
                if (fd < 0) {
                    error(1, errno, "accept failed");
                } else {
                    make_nonblocking(fd);
                    event.data.fd = fd;
                    event.events = EPOLLIN | EPOLLET; /* edge-triggered */
                    if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event) == -1) {
                        error(1, errno, "epoll_ctl add connection fd failed");
                    } /*if  end*/
                }/*else end*/
                continue;
            }/*else if end*/ else {
                socket_fd = events[i].data.fd;
                printf("get event on socket fd == %d \n", socket_fd);

                while (1) {
                    char buf[512];
                    if ((n = read(socket_fd, buf, sizeof(buf))) < 0) {
                        if (errno != EAGAIN) {
                            error(1, errno, "read error");
                            close(socket_fd);
                            //printf("client close \n ");
                        }
                        break;
                    }
                    else if (n == 0) {
                        close(socket_fd);
                        printf("client close \n ");
                        break;
                    }
                    else {
                        for (i = 0; i < n; ++i) {
                            buf[i] = rot13_char(buf[i]);
                        }
                        if (write(socket_fd, buf, n) < 0) {
                            error(1, errno, "write error");
                        }
                    }
                }/*while end*/
            }
        }/*for end*/
    };
}/*-Drecreate.x11.input.method=true # zsl add*/

